/*
* Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include <common.h>
#include <command.h>
#include <hi3559av100_vo.h>

extern int set_vobg(unsigned int dev, unsigned int rgb);
extern int start_vo(unsigned int dev, unsigned int type, unsigned int sync);
extern int stop_vo(unsigned int dev);
extern int start_gx(unsigned int layer, unsigned long addr, unsigned int strd, unsigned int x, unsigned int y, unsigned int w, unsigned int h);
extern int stop_gx(unsigned int layer);
extern int start_videolayer(unsigned int layer, unsigned long addr, unsigned int strd,
        unsigned int x, unsigned int y, unsigned int w, unsigned int h);
extern int stop_videolayer(unsigned int layer);

extern int hdmi_display(unsigned int vosync, unsigned int input, unsigned int output);
extern void hdmi_stop(void);

extern int mipi_tx_display(unsigned int vosync);
extern int mipi_tx_stop(void);


#define VOU_DEV_MAX_NUM         3
static int gs_aInterfaceType[VOU_DEV_MAX_NUM] = {0};

int check_vo_support(unsigned int dev, unsigned int type, unsigned int sync)
{
    /* check interface type, ONLY VGA & HDMI interface is supported. */
    if (VOU_DEV_DHD0 == dev)
    {
        if((type & ~(VO_INTF_MIPI | VO_INTF_HDMI | VO_INTF_BT656 | VO_INTF_BT1120)) || (0 == type))
        {
            printf("Hd%d only supports HDMI,BT656,BT1120,MIPI_Tx intftype, intf %d is illegal!\n",dev,type);
            return -1;
        }
        // just one interface at the the time for a dev.
        if ((type & ~(VO_INTF_HDMI | VO_INTF_BT1120))
            && (type & ~(VO_INTF_HDMI | VO_INTF_MIPI))
            && (type & ~VO_INTF_BT656 )
        )
        {
            printf("For VO %d, Any two of (HDMI,BT1120,BT656,MIPI) can not be used at the same time!\n",
                     dev);
            return -1;
        }
    }
    else if (VOU_DEV_DHD1 == dev)
    {
        if((type
             & ~(VO_INTF_BT1120    | VO_INTF_BT656    | VO_INTF_MIPI
                 | VO_INTF_LCD_6BIT  | VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT
                 | VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT))
            || (0 == type))
        {
            printf("Hd%d only supports BT656,BT1120,MIPI_Tx,LCD intftype, intf %d is illegal!\n",dev,type);
            return -1;
        }
        // just one interface at the the time for a dev.
        if (
            (type & ~VO_INTF_BT1120 )   && (type & ~VO_INTF_BT656) && (type & ~VO_INTF_MIPI)
            && (type & ~VO_INTF_LCD_6BIT ) && (type & ~VO_INTF_LCD_8BIT)
            && (type & ~VO_INTF_LCD_16BIT) && (type & ~VO_INTF_LCD_18BIT) && (type & ~VO_INTF_LCD_24BIT)
        )
        {
            printf("Vo(%d), none of (BT1120,BT656,MIPI,LCD) can use at the same time!\n",
                     dev);
            return -1;
        }

    }
    else
    {
        printf("Unknow dev(%d)!\n", dev);
        return -1;
    }

    /* check interface sync. */
    /*
    if (VOU_DEV_DHD1 == dev)
    {
        if (sync < VO_OUTPUT_1080P24 || sync >= VO_OUTPUT_1600x1200_60)
        {
            printf("Vo%d's intfsync %d illegal!\n", dev, sync);
            return -1;
        }
    }
    //*/

    if (VO_INTF_HDMI & type)
    {
        if (sync < VO_OUTPUT_1080P24    ||
            VO_OUTPUT_1080I50   == sync ||
            VO_OUTPUT_1080I60   == sync ||
            VO_OUTPUT_960H_PAL  == sync ||
            VO_OUTPUT_960H_NTSC == sync ||
            (sync >= VO_OUTPUT_320x240_60 && sync <= VO_OUTPUT_1080x1920_60) ||
            sync >= VO_OUTPUT_USER)
        {
            printf("Vo%d's intfsync %d illegal!\n", dev, sync);
            return -1;
        }
    }

    if ( VO_INTF_BT1120 & type)
    {
        if(VOU_DEV_DHD0 == dev)
        {
            if ( sync < VO_OUTPUT_1080P24  ||
                 VO_OUTPUT_1080I60 == sync ||
                 VO_OUTPUT_1080I50 == sync ||
                 (sync > VO_OUTPUT_640x480_60)
               )
            {
                printf("Vo%d's intfsync %d illegal!\n", dev, sync);
                return -1;
            }
        }
        else if(VOU_DEV_DHD1 == dev)
        {
            if ( sync < VO_OUTPUT_1080P24 ||
                 (sync > VO_OUTPUT_480P60)
               )
            {
                printf("Vo%d's intfsync %d illegal!\n", dev, sync);
                return -1;
            }
        }
    }

    if (VO_INTF_VGA & type)
    {
        if ((sync < VO_OUTPUT_1080P24)
            || (sync >= VO_OUTPUT_3840x2160_30)
            || (VO_OUTPUT_1920x2160_30 == sync)
            || (VO_OUTPUT_2560x1440_30 == sync))
        {
            printf("Vo%d's intfsync %d illegal!\n", dev, sync);
            return -1;
        }
    }

    if(VO_INTF_CVBS & type)
    {
        if ((sync != VO_OUTPUT_PAL) && (sync != VO_OUTPUT_NTSC)
            && (sync != VO_OUTPUT_960H_PAL) && (sync != VO_OUTPUT_960H_NTSC))
        {
            printf("Vo%d's intfsync %d illegal!\n", dev, sync);
            return -1;
        }
    }
    if(VO_INTF_MIPI & type)
    {
        if (VOU_DEV_DHD0 == dev)
        {
            if (sync != VO_OUTPUT_576P50       &&
                sync != VO_OUTPUT_720P50       &&
                sync != VO_OUTPUT_720P60       &&
                sync != VO_OUTPUT_1024x768_60  &&
                sync != VO_OUTPUT_1280x1024_60 &&
                sync != VO_OUTPUT_720x1280_60  &&
                sync != VO_OUTPUT_1080x1920_60 &&
                sync != VO_OUTPUT_1080P60)
            {
                printf("Vo%d's intfsync %d illegal!\n", dev, sync);
                return -1;
            }
        }
        else if (VOU_DEV_DHD1 == dev)
        {
            if (sync != VO_OUTPUT_576P50       &&
                sync != VO_OUTPUT_720P50       &&
                sync != VO_OUTPUT_720P60       &&
                sync != VO_OUTPUT_1024x768_60  &&
                sync != VO_OUTPUT_1280x1024_60 &&
                sync != VO_OUTPUT_720x1280_60  &&
                sync != VO_OUTPUT_1080x1920_60 &&
                sync != VO_OUTPUT_1080P60)
            {
                printf("Vo%d's intfsync %d illegal!\n", dev, sync);
                return -1;
            }
        }
    }
    return 0;
}

int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int dev, rgb;

    if (argc < 3)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }

    dev = (unsigned int)simple_strtoul(argv[1], NULL, 10);
    rgb = (unsigned int)simple_strtoul(argv[2], NULL, 10);
    if (dev >= VOU_DEV_BUTT)
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    set_vobg(dev, rgb);

    printf("dev %d set background color!\n", dev);

    return 0;
}

int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int dev, intftype, sync;

    if (argc < 4)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }

    dev      = (unsigned int)simple_strtoul(argv[1], NULL, 10);
    intftype = (unsigned int)simple_strtoul(argv[2], NULL, 10);
    sync     = (unsigned int)simple_strtoul(argv[3], NULL, 10);
    if (dev >= VOU_DEV_BUTT || sync >= VO_OUTPUT_BUTT)
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    if (check_vo_support(dev, intftype, sync))
    {
        printf("Unsupport parameter!\n");
        return -1;
    }

    start_vo(dev, intftype, sync);

    gs_aInterfaceType[dev] =  intftype;

    if (intftype & VO_INTF_HDMI)
    {
        if (intftype == (VO_INTF_HDMI | VO_INTF_MIPI))
        {
            hdmi_display(sync, 0, 2);
        }
        else
        {
            hdmi_display(sync, 2, 2);
        }
    }

    if (intftype & VO_INTF_MIPI)
    {
        // to call mipi_display.
        mipi_tx_display(sync);
    }


    printf("Dev %d opened!\n", dev);

    return 0;
}

int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int dev;
    if (argc < 2)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }

    dev = (unsigned int)simple_strtoul(argv[1], NULL, 10);
    if (dev >= VOU_DEV_BUTT)
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    if (gs_aInterfaceType[dev] & VO_INTF_HDMI)
    {
        gs_aInterfaceType[dev] = 0;
        hdmi_stop();
    }

    if (gs_aInterfaceType[dev] & VO_INTF_MIPI)
    {
        // To call mipi_tx_stop().
        //gs_aInterfaceType[dev] = 0;
        mipi_tx_stop();
    }

    stop_vo(dev);

    printf("Dev %d closed!\n", dev);

    return 0;
}

int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int layer, strd, x, y, w, h;
    unsigned long addr;
    if (argc < 8)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }
    //dcache_disable();

    layer = (unsigned int)simple_strtoul(argv[1], NULL, 10);
    addr  = (unsigned long)simple_strtoul(argv[2], NULL, 16);
    strd  = (unsigned int)simple_strtoul(argv[3], NULL, 10);
    x     = (unsigned int)simple_strtoul(argv[4], NULL, 10);
    y     = (unsigned int)simple_strtoul(argv[5], NULL, 10);
    w     = (unsigned int)simple_strtoul(argv[6], NULL, 10);
    h     = (unsigned int)simple_strtoul(argv[7], NULL, 10);

    if (layer >= VO_GRAPHC_BUTT || strd > (PIC_MAX_WIDTH*2)
        || x > PIC_MAX_WIDTH   || (x & 0x1)
        || y > PIC_MAX_HEIGHT  || (y & 0x1)
        || w > PIC_MAX_WIDTH   || (w & 0x1) || w < PIC_MIN_LENTH
        || h > PIC_MAX_HEIGHT  || (h & 0x1) || h < PIC_MIN_LENTH
        )
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    start_gx(layer, addr, strd, x, y, w, h);

    printf("graphic layer %d opened!\n", layer);

    return 0;
}

int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int layer;

    if (argc < 2)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }

    layer = (unsigned int)simple_strtoul(argv[1], NULL, 10);

    if (layer >= VO_GRAPHC_BUTT)
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    stop_gx(layer);

    printf("graphic layer %d closed!\n", layer);

    return 0;
}

int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int layer, strd, x, y, w, h;
    unsigned long addr;

    if (argc < 8)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }

    layer = (unsigned int)simple_strtoul(argv[1], NULL, 10);
    addr  = (unsigned long)simple_strtoul(argv[2], NULL, 16);
    strd  = (unsigned int)simple_strtoul(argv[3], NULL, 10);
    x     = (unsigned int)simple_strtoul(argv[4], NULL, 10);
    y     = (unsigned int)simple_strtoul(argv[5], NULL, 10);
    w     = (unsigned int)simple_strtoul(argv[6], NULL, 10);
    h     = (unsigned int)simple_strtoul(argv[7], NULL, 10);

    if (layer > VOU_LAYER_VSD0 || layer == VOU_LAYER_VP
        || strd > (PIC_MAX_WIDTH*2)
        || x > PIC_MAX_WIDTH   || (x & 0x1)
        || y > PIC_MAX_HEIGHT  || (y & 0x1)
        || w > PIC_MAX_WIDTH   || (w & 0x1) || w < PIC_MIN_LENTH
        || h > PIC_MAX_HEIGHT  || (h & 0x1) || h < PIC_MIN_LENTH
        )
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    start_videolayer(layer, addr, strd, x, y, w, h);

    printf("video layer %d opened!\n", layer);

    return 0;
}

int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned int layer;

    if (argc < 2)
    {
        printf("Insufficient parameter!\n");
        printf ("Usage:\n%s\n", cmdtp->usage);
        return -1;
    }

    layer = (unsigned int)simple_strtoul(argv[1], NULL, 10);

    if (layer > VOU_LAYER_VSD0 || layer == VOU_LAYER_VP)
    {
        printf("Invalid parameter!\n");
        return -1;
    }

    stop_videolayer(layer);

    printf("video layer %d closed!\n", layer);

    return 0;
}

U_BOOT_CMD(
	startvo,    CFG_MAXARGS,	1,  do_startvo,
    "startvo   - open vo device with a certain output interface.\n"
    "\t- startvo [dev intftype sync]",
    "\nargs: [dev, intftype, sync]\n"
    "\t-<dev> : 0: DHD0,1: DHD1\n"
    "\t-<intftype>: 8(BT656),16(BT1120),32(HDMI),64(LCD),1024(LCD_8BIT),16384(MIPI_Tx)\n"
    "\t-<sync>:\n"
    "\t\t0(PAL),          1(NTSC),         2(1080P24),      3(1080P25)\n"
    "\t\t4(1080P30),      5(720P50),       6(720P60),       7(1080I50)\n"
    "\t\t8(1080I60),      9(1080P50),      10(1080P60),     11(576P50)\n"
    "\t\t12(480P60),      13(800x600),     14(1024x768),    15(1280x1024)\n"
    "\t\t16(1366x768),    17(1440x900),    18(1280x800),    19(1600x1200)\n"
    "\t\t20(1680x1050),   21(1920x1200),   22(640x480),     23(960H_PAL)\n"
    "\t\t24(960H_NTSC),   25(1920x2160),   26(2560x1440_30),27(2560x1440_60)\n"
    "\t\t28(2560x1600_60),29(3840x2160_24),30(3840x2160_25),31(3840x2160_30)\n"
    "\t\t32(3840x2160_50),33(3840x2160_60),34(4096x2160_24),35(4096x2160_25)\n"
    "\t\t36(4096x2160_30),37(4096x2160_50),38(4096x2160_60),39(320x240_60)\n"
    "\t\t40(320x240_50),  41(240x320_50),  42(240x320_60),  43(800x600_50)\n"
    "\t\t44(720x1280_60), 45(1080x1920_60),46(7680x4320_30)\n"
    );

U_BOOT_CMD(
	stopvo,    CFG_MAXARGS,	1,  do_stopvo,
    "stopvo   - close interface of vo device.\n"
    "\t- stopvo [dev]",
    "\nargs: [dev]\n"
    "\t-<dev> : 0~1(HD0~1)\n"
    );

U_BOOT_CMD(
	startgx,    CFG_MAXARGS,	1,  do_startgx,
    "startgx   - open graphics layer.\n"
    "\t- startgx [layer addr stride x y w h]\n",
    "\nargs: [layer, addr, stride, x, y, w, h]\n"
    "\t-<layer>   : 0(G0), 1(G1)\n"
    "\t-<addr>    : picture address\n"
    "\t-<stride>  : picture stride\n"
    "\t-<x,y,w,h> : display area\n"
    );

U_BOOT_CMD(
	stopgx,    CFG_MAXARGS,	1,  do_stopgx,
    "stopgx   - close graphics layer.\n"
    "\t- stopgx [layer]",
    "\nargs: [layer]\n"
    "\t-<layer> : 0(G0), 1(G1)\n"
    );

U_BOOT_CMD(
	startvl,    CFG_MAXARGS,	1,  do_startvl,
    "startvl   - open video layer.\n"
    "\t- startvl [layer addr stride x y w h]\n",
    "\nargs: [layer, addr, stride, x, y, w, h]\n"
    "\t-<layer>   : 0(V0), 1(V1)\n"
    "\t-<addr>    : picture address\n"
    "\t-<stride>  : picture stride\n"
    "\t-<x,y,w,h> : display area\n"
    );

U_BOOT_CMD(
	stopvl,    CFG_MAXARGS,	1,  do_stopvl,
    "stopvl   - close video layer.\n"
    "\t- stopvl [layer]",
    "\nargs: [layer]\n"
    "\t-<layer> : 0(V0), 1(V1)\n"
    );


U_BOOT_CMD(
	setvobg,    CFG_MAXARGS,	1,  do_vobg,
    "setvobg   - set vo backgroud color.\n"
    "\t- setvobg [dev color]",
    "\nargs: [dev, color]\n"
    "\t-<dev> : 0~1(HD0~1)\n"
    "\t-<color>: rgb color space\n"
    );


